package com.gvsoft.communication.client;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.serializer.SerializerFeature;
import com.gvsoft.communication.console.Console;
import com.gvsoft.communication.console.IControl;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.Map;
import java.util.Properties;
import java.util.StringTokenizer;
import java.util.concurrent.*;

/**
 * Created with IntelliJ IDEA.
 * ProjectName:gvMsgClient
 * Author: zhaoqiubo
 * Date: 15/7/31
 * Time: 下午3:22
 * Desc: 客户端配置类。
 */
public class ClientConfig {

    private  String serverIp;

    private  int serverPort;

    private static Logger logger = LogManager.getLogger("client");

    /**
     * 报文区间分隔符，报文分为三个区间，HEADER区间、RID区间、BODY区间；
     * HEADER标记报文分类，RID为报文对象id，在一定周期内唯一；BODY为报文的内容区域。
     */
    public static final String HBREGEX = "|";
    /**
     * 存储所有报文处理类的实例，一次性初始化进入Map（为消息处理线程（CommunicateQueueConsumer中使用）准备的单例消息处理类Map）
     */
    private final Map<String, Object> SINGLE_INSTANCE_ORDER_CLASS_MAP = new ConcurrentHashMap();
    /**
     * 配置报文处理类分类的外部分隔符，分隔符两边代表某一类报文处理类的属性描述。
     * 例如：R:com.gvsoft.communication.client.model.RegisterHandleModel|E:com.gvsoft.communication.client.model.ErrorHandleModel
     */
    private final String SEPARATOR_OUTER = "|";
    /**
     * 配置报文处理类分类的内部部分隔符,分割某一类报文处理类的header和body。
     * 例如：R:com.gvsoft.communication.client.model.RegisterHandleModel
     */
    private final String SEPARATOR_INNER = ":";
    /**
     * 维持链路的周期
     */
    private  int ackCycle = 5;
    /**
     * 重连的周期
     */
    private  int reconnectCycle = 10;

    /**
     * 链路维护超期次数：就是几次收不到回应启动重连，这个次数的标记。
     */
    private  int keepTimeoutCount = 3;

    /*------------------------------------------------------------------------------------
    【注意】keepTimeoutcount*keepAliveCycle 必须大于 reconnectCycle ，否则容易造成一直在重新连接
     ------------------------------------------------------------------------------------*/
    /**
     * 网络报文读取缓冲区大小
     */
    private static Integer readBlock = 8192;
    /**
     * 指令写入缓冲区大小
     */
    private static Integer writeBlock = 8192;
    /**
     * 读包buffer缓冲区默认大小
     */
    private static Integer readBufferPoolSize = 1000;

    private static Integer writeBufferPoolSize = 1000;
    /**
     * 读取处理线程池默认大小
     */
    private  Integer readHandleThreadCount = 3;

    /**
     * 业务包标记
     */
    public final static String BUSI_TAG = "M";
    /**
     * 存储所有报文处理类的实例，一次性初始化进入Map（为连接通道（GVServer中使用）准备的单例类Map）
     */
    protected Map<String, Object> PACKET_HANDLE_CLASS_MAP = new ConcurrentHashMap();
    /**
     * 管理端口
     */
    private static int consolePort = 9091;
    /**
     * 单个Client配置资源
     */
    private Properties p = new Properties();


    public String getServerIp() {
        return serverIp;
    }

    public void setServerIp(String serverIp) {
        this.serverIp = serverIp;
    }

    public int getServerPort() {
        return serverPort;
    }

    public void setServerPort(int serverPort) {
        this.serverPort = serverPort;
    }

    public  int getKeepAliveCycle() {
        return ackCycle;
    }

    private void setKeepAliveCycle(int keepAliveCycle) {
        ackCycle = keepAliveCycle;
    }

    public  int getReconnectCycle() {
        return reconnectCycle;
    }

    private void setReconnectCycle(int reconnectCycle) {
        reconnectCycle = reconnectCycle;
    }

    public  int getKeepTimeoutCount() {
        return keepTimeoutCount;
    }

    private void setKeepTimeoutCount(int keepTimeoutCount) {
        keepTimeoutCount = keepTimeoutCount;
    }

    public static int getConsolePort() {
        return consolePort;
    }

    public boolean initConfig() {

        //【客户端开发请注意】此处需要客户端自己添加读取配置信息，可以将配置文件中的ip和port
        // 读取进来，将程序中的clientid（有时指用户唯一标识）读取进来。
        InputStreamReader reader = null;
        try {
           reader  = new InputStreamReader(new FileInputStream
                    (cfgFile), "utf-8");
            p.load(reader);
            this.setServerIp(p.getProperty("server_ip").trim());
            this.setServerPort(Integer.parseInt(p.getProperty("server_port").trim()));
            this.setKeepAliveCycle(Integer.parseInt(p.getProperty("keep_alive_cycle").trim()));
            this.setKeepTimeoutCount(Integer.parseInt(p.getProperty("keep_timeout_count").trim()));
            this.setReconnectCycle(Integer.parseInt(p.getProperty("reconnect_cycle").trim()));
            this.readHandleThreadCount = Integer.parseInt(p.getProperty("readhandle_thread_count").trim());
            initPacketHandleMap(p.getProperty("packet_handle_class"));

            //处理json转化中的null问题，将对象的null属性包含在json的转化过程中。
            JSON.DEFAULT_GENERATE_FEATURE |= SerializerFeature.WriteMapNullValue.getMask();
            JSON.DEFAULT_GENERATE_FEATURE |= SerializerFeature.WriteNullStringAsEmpty.getMask();

            return true;
        } catch (IOException e) {
            logger.error("Failed to initialize the configuration parameter！reason:" + e.getMessage());
            return false;
        }finally {
            try {
                reader.close();
            } catch (IOException e) {
                logger.error("Failed to close the InputStreamReader ！reason:" + e.getMessage());
            }
        }

    }

    /**
     * 加载全局配置
     * @param commonCfgFile
     * @return
     */
    public static boolean initCommonCfg(String commonCfgFile){
        InputStreamReader reader = null;
        try {
            reader = new InputStreamReader(new FileInputStream
                    (commonCfgFile), "utf-8");

            Properties commonP = new Properties();
            commonP.load(reader);
            consolePort = Integer.parseInt(commonP.getProperty("console_port").trim());
            readBufferPoolSize = Integer.parseInt(commonP.getProperty("read_buffer_pool_size").trim());
            writeBufferPoolSize = Integer.parseInt(commonP.getProperty("write_buffer_pool_size").trim());
            readBlock = Integer.parseInt(commonP.getProperty("read_block").trim());
            writeBlock = Integer.parseInt(commonP.getProperty("write_block").trim());
            initController(commonP.getProperty("console_class"));
            return true;
        } catch (IOException e) {
            logger.error("Failed to initialize the common configuration parameter！reason:" + e.getMessage());
            return false;
        }finally {
            try {
                reader.close();
            } catch (IOException e) {
                logger.error("Failed to close the InputStreamReader ！reason:" + e.getMessage());
            }
        }
    }
    /**
     * 初始化报文实例池
     *
     * @param handleCfgStr
     */
    private void initPacketHandleMap(String handleCfgStr) {
        String[] handleOuterArray = str2ArrayByChar(handleCfgStr, SEPARATOR_OUTER);
        try {
            for (int i = 0; i < handleOuterArray.length; i++) {
                String[] handleInnerArray = str2ArrayByChar(handleOuterArray[i], SEPARATOR_INNER);
                Class c = Class.forName(handleInnerArray[1]);
                Object handleClass = c.newInstance();
                PACKET_HANDLE_CLASS_MAP.put(handleInnerArray[0], handleClass);
            }
        } catch (Exception e) {
            logger.error("initialize packet handle model error:" + e.getMessage());
        }
    }
    /**
     * 初始化控制及采集器
     * @param consoleStr
     */
    private static void initController(String consoleStr) {
        String[] handleOuterArray = str2ArrayByChar(consoleStr, "|");
        try {
            for (int i = 0; i < handleOuterArray.length; i++) {
                String[] handleInnerArray = str2ArrayByChar(handleOuterArray[i], ":");
                Class c = Class.forName(handleInnerArray[1]);
                Object controllerClass = c.newInstance();
                Console.regCmd(handleInnerArray[0],(IControl) controllerClass);
            }
        } catch (Exception e) {
            logger.error("Failed to initialize the Controller! reason:"+e.getMessage());
            e.printStackTrace();
        }
    }
    /**
     * 返回指令实例池Map
     * @return
     */
    public  Map<String, Object> getOrderMap() {
        return SINGLE_INSTANCE_ORDER_CLASS_MAP;
    }

    //返回系统当前时间秒
    public static final int systemTimeUtc() {
        return (int) (System.currentTimeMillis() / 1000L);
    }

    private String cfgFile;

    public ClientConfig(String file) {
        this.cfgFile = file;
    }

    public static Integer getWriteBufferPoolSize() {
        return writeBufferPoolSize;
    }

    public static Integer getWriteBlock() {
        return writeBlock;
    }

    public static Integer getReadBlock() {
        return readBlock;
    }

    public static Integer getReadBufferPoolSize() {
        return readBufferPoolSize;
     }

    public  Integer getReadHandleThreadCount() {
        return readHandleThreadCount;
    }

    /**
     * 从报文实例池中取出操作实例
     *
     * @param classKey
     * @return
     */
    public Object getPacketHandleInstance(String classKey) {
        return PACKET_HANDLE_CLASS_MAP.get(classKey);
    }

    /*
    根据分隔符，将字符串分割为数组
     */
    public static String[] str2ArrayByChar(String string, String divisionChar) {
        int i = 0;
        StringTokenizer tokenizer = new StringTokenizer(string, divisionChar);

        String[] str = new String[tokenizer.countTokens()];

        while (tokenizer.hasMoreTokens()) {
            str[i] = new String();
            str[i] = tokenizer.nextToken().trim();
            i++;
        }

        return str;
    }
    public String getProperty(String cfgStr){
        String property = p.getProperty(cfgStr);
        if (property != null){
            return property.trim();
        }
        return property;
    }
}
